
#include <functional>
#include <future>
#include <memory>
#include <chrono>
using namespace std;
using namespace std::chrono;

#include "SyncQueue_5.hpp"

#ifndef SCHEDULED_THREAD_POOL_HPP
#define SCHEDULED_THREAD_POOL_HPP

namespace tulun
{
    class ScheduledThreadPool
    {
    private:
        SyncQueue m_queue;
        std::list<std::shared_ptr<std::thread>> m_threadgroup;
        std::atomic_bool m_running;
        std::once_flag m_flag;

        void Start(int numthreads)
        {
            m_running = true;
            for (int i = 0; i < numthreads; ++i)
            {
                m_threadgroup.push_back(std::make_shared<std::thread>(&ScheduledThreadPool::RunInThread, this));
            }
        }

        void RunInThread()
        {
            while (m_running)
            {
                PairTask task;
                if (m_queue.Take(task) != 0)
                {
                    continue;
                }
                task.second();
            }
        }
        void StopThreadGroup()
        {
            m_queue.Stop();
            m_running = false;
            for (auto &tha : m_threadgroup)
            {
                if (tha && tha->joinable())
                {
                    tha->join();
                }
            }
            m_threadgroup.clear();
        }

    public:
        ScheduledThreadPool(const int qusize = 10, const int numthreads = 8)
            : m_queue(qusize), m_running(false)
        {
            Start(numthreads);
        }
        ~ScheduledThreadPool()
        {
            Stop();
        }
        void Stop()
        {
            std::call_once(m_flag, [this]() -> void
                           { StopThreadGroup(); });
        }
        template <class Func, class... Args>
        void execute(int interval, Func &&func, Args &&...args)
        {
            using RetType = decltype(func(args...));
            auto task = std::make_shared<std::function<void(void)>>(std::bind(std::forward<Func>(func), std::forward<Args>(args)...));
            auto tk = PairTask(interval, [task](void) -> void
                               { (*task)(); });
            if (m_queue.Put(tk) != 0)
            {
                (*task)();
            }
        }
        template <class Func, class... Args>
        auto submit(int interval, Func &&func, Args &&...args)
        {
            using RetType = decltype(func(args...));
            auto task = std::make_shared<std::packaged_task<RetType(void)>>(std::bind(std::forward<Func>(func), std::forward<Args>(args)...));
            std::future<RetType> result = task->get_future();
            PairTask tk(interval, [task](void) -> void
                        { (*task)(); });
            if (m_queue.Put(tk) != 0)
            {
                (*task)();
            }
            return result;
        }
    };
} // namespace tulun

#endif